package org.wiztools.oembed;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Convenience class for building oEmbed service Url.
 * @author subhash
 */
public class OEmbedUrlBuilder {
    
    private static final Logger LOG = Logger.getLogger(OEmbedUrlBuilder.class.getName());
    
    private final String oembedServiceUrl;
    private final Map<String, String> params = new LinkedHashMap<>();
    
    private int urlCount;
    private int formatCount;
    private int maxWidthCount;
    private int maxHeightCount;
    
    /**
     * Construct the class with service Url.
     * @param oembedServiceUrl The service Url.
     */
    public OEmbedUrlBuilder(String oembedServiceUrl) {
        if(oembedServiceUrl == null) {
            throw new NullPointerException("oEmbed Service Url cannot be null!");
        }
        this.oembedServiceUrl = oembedServiceUrl;
    }
    
    private void validate() throws IllegalStateException {
        if(urlCount != 1) {
            throw new IllegalStateException("Exactly one url needs to be added.");
        }
        if(formatCount > 1) {
            throw new IllegalStateException("More than one format cannot be specified.");
        }
        if(maxWidthCount > 1) {
            throw new IllegalStateException("More than one maxwidth cannot be specified.");
        }
        if(maxHeightCount > 1) {
            throw new IllegalStateException("More than one maxheight cannot be specified.");
        }
    }
    
    /**
     * Build the Url from the current object.
     * @return The built Url.
     */
    public URL build() throws IllegalStateException {
        // validate before building:
        validate();
        
        // start building:
        try{
            StringBuilder sb = new StringBuilder();
            sb.append(oembedServiceUrl).append("?");
            for(Map.Entry<String, String> entry: params.entrySet()) {
                sb.append(entry.getKey()).append("=").append(entry.getValue());
                sb.append("&");
            }
            sb.deleteCharAt(sb.length()-1); // delete the last `&'
            return new URL(sb.toString());
        }
        catch(MalformedURLException ex) {
            throw new IllegalStateException(ex);
        }
    }
    
    /**
     * Add the Url param to the service Url.
     * @param url The url.
     * @return `this' instance useful for chaining.
     */
    public OEmbedUrlBuilder addUrl(String url) {
        urlCount++;
        return addParam("url", url);
    }
    
    /**
     * Adds the parameter format=json.
     * @return `this' instance useful for chaining.
     */
    public OEmbedUrlBuilder addFormatJson() {
        formatCount++;
        return addParam("format", "json");
    }
    
    /**
     * Adds the parameter format=xml.
     * @return `this' instance useful for chaining.
     */
    public OEmbedUrlBuilder addFormatXml() {
        formatCount++;
        return addParam("format", "xml");
    }
    
    /**
     * Adds the <code>maxwidth</code> parameter.
     * @param width
     * @return `this' instance useful for chaining.
     */
    public OEmbedUrlBuilder addMaxWidth(int width) {
        maxWidthCount++;
        return addParam("maxwidth", String.valueOf(width));
    }
    
    /**
     * Adds the <code>maxheight</code> parameter.
     * @param height
     * @return `this' instance useful for chaining.
     */
    public OEmbedUrlBuilder addMaxHeight(int height) {
        maxHeightCount++;
        return addParam("maxheight", String.valueOf(height));
    }
    
    /**
     * Adds any custom parameter.
     * @param key The parameter key.
     * @param value The parameter value.
     * @return `this' instance useful for chaining.
     */
    public OEmbedUrlBuilder addParam(final String key, final String value) {
        try {
            params.put(key, URLEncoder.encode(value, "UTF-8"));
        }
        catch(UnsupportedEncodingException ex) {
            LOG.log(Level.SEVERE, null, ex);
        }
        return this;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("<<").append(oembedServiceUrl)
                .append("; ")
                .append(params)
                .append(">>");
        return sb.toString();
    }
}
